#include <stdio.h>
#include <stdlib.h>
#include "emboard.h"
#include "skyeye_mach.h"
#include "skyeye_arch.h"
#include "skyeye_pref.h"
#include "skyeye_config.h"
#include "skyeye_callback.h"
#include "skyeye_bus.h"
#include "skyeye_ram.h"
#include "sim_control.h"

static const char *g_skyeye_config = 
    "cpu: arm920t\nmach: s3c2440\n"
    "mem_bank: map=M, type=RW, addr=0x00000000, size=0x00010000\n"
    "mem_bank: map=F, type=RW, addr=0x10000000, size=0x00200000, boot=yes\n"
    "mem_bank: map=M, type=RW, addr=0x30000000, size=0x04000000\n"
    "mem_bank: map=I, type=RW, addr=0x48000000, size=0x20000000\n\n"
    "uart: mod=stdio\n"
    "lcd: type=s3c2410x\n"
    "flash: type=SST39VF160, base=0x10000000, size=0x00200000, filebase=./norflash\n";

extern void ARM9DASM(unsigned int, char *);
/*
typedef void (*callback_func_t)(void*);

struct _device_callback_list {
    const char *name;
    callback_func_t *func_ptr;

    struct _device_callback_list* next;
} *device_callback_list = NULL;

void append_device(struct _device_callback_list* dp, const char* name, callback_func_t* ptr)
{
    dp = (struct _device_callback_list *) malloc(sizeof(struct _device_callback_list));
    dp->next = NULL;
    dp->func_ptr = ptr;
    dp->name = name;
}

void em_register_device_callback(const char* name, callback_func_t* ptr)
{
    if (device_callback_list == NULL) {
        append_device(device_callback_list, name, ptr);
        printf("first device registered: %s\n", name);
    } else {
        struct _device_callback_list* dp = device_callback_list;
        struct _device_callback_list* old_dp = NULL;

        unsigned char flag = 0;
        for (;dp != NULL; dp = dp->next) {
            if (strcmp(dp->name, name) == 0) {
                dp->func_ptr = ptr;
                printf("device %s already registered, change ptr.\n", name);
                flag = 1;
                old_dp = dp;
                break;
            }
        }
        if (!flag) {
            printf("append new device: %s\n", name);
            append_device(dp, name, ptr);
            old_dp->next = dp;
        }
    }
}
*/

void bus_write_callback(generic_arch_t* arch_instance)
{
    bus_recorder_t* br = get_last_bus_access();

    if (br && br->addr == GPBDAT) {
        if ( !(br->value & (1 << 5)) ) {
            printf("EB: LED1 is lit.\n");
        }
        if ( !(br->value & (1 << 6)) ) {
            printf("EB: LED2 is lit.\n");
        }
        if ( !(br->value & (1 << 7)) ) {
            printf("EB: LED3 is lit.\n");
        }
        if ( !(br->value & (1 << 8)) ) {
            printf("EB: LED4 is lit.\n");
        }
    }
}

void em_init()
{
    sky_pref_t *sp = get_skyeye_pref();

    printf("Init called\n");

    if (sp == NULL) return;

    sp->module_search_dir = "../../skyeye/dest/lib/skyeye/";
    sp->interactive_mode = False;
    
    FILE *conf = fopen("skyeye.conf", "w");
    /*
    size_t l = strlen(g_skyeye_config) + strlen(filename);
    char* conf_str = (char*) malloc(l + 1);
    memset(conf_str, 0, l + 1);
    sprintf(conf_str, g_skyeye_config, filename);
    */
    
    fwrite(g_skyeye_config, sizeof(char), strlen(g_skyeye_config), conf);
    sp->conf_filename = "skyeye.conf";
    fflush(conf);

    SIM_init();
    register_callback(bus_write_callback, Bus_write_callback);
}

void em_start()
{
    printf("Start called\n");
    SIM_start();
}

void em_run()
{
    printf("Run called\n");
    SIM_run();
}

void em_stop()
{
    printf("Stop called\n");
    SIM_stop(get_arch_instance(""));
}

void em_step()
{
    char *str = NULL;
    unsigned int step;
    unsigned int addr;

    printf("Step called\n");
    skyeye_stepi(1);

    generic_arch_t* arch = get_arch_instance("");
    addr = arch->get_pc();
    step = arch->get_instr();

    str = (char*)malloc(128); 
    ARM9DASM(step, str); 
    printf("0x%x: %s, 0x%x\n", addr, str, step); 
    free(str); 
}

void em_dump_regs()
{
    int i = 0;
    generic_arch_t* arch = get_arch_instance("");

    for (i = 0; i < 16; ++i)
        printf("R%d=0x%x ", i, arch->get_regval_by_id(i));

    printf("\n");
}

void set_bit_32(unsigned int *data, unsigned char value, unsigned char offset)
{
    unsigned v = *data;

    if (value == 1)
        v |= (1 << offset);
    else if (value == 0)
        v &= ~(1 << offset);
    else
        return;

    *data = v;
}

unsigned char* em_get_lcd_base_addr()
{
    unsigned int data = 0;
    io_read(32, 0x4d000014, &data);

    if (data == 0) return 0;
    return get_dma_addr(data << 1);
}

void em_sendevent(const char* event)
{
    machine_config_t* mach = get_current_mach();

    if (strcmp(event, "key1-pressed") == 0) {
        unsigned int data = mach->mach_io_read_word(NULL, GPGDAT);
        set_bit_32(&data, 1, 0);
        mach->mach_io_write_word(NULL, GPGDAT, data);

        // todo: EINT8
    }
    if (strcmp(event, "key1-released") == 0) {
        unsigned int data = mach->mach_io_read_word(NULL, GPGDAT);
        set_bit_32(&data, 0, 0);
        mach->mach_io_write_word(NULL, GPGDAT, data);

        // todo: EINT8
    }
}
